using System;
using Firestarter.Test.Base;
using FireStarterModeller.BO;
using Habanero.Base;
using Habanero.BO;
using NUnit.Framework;
using Rhino.Mocks;

namespace FireStarterModeller.Test.BO
{
    [TestFixture]
    public class TestDmRelationshipManyToOneMustBeAssociation
    {
        [SetUp]
        public void SetupTest()
        {
            //Runs every time that any testmethod is executed
            TestUtilsDomain.SetupTest();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            //Code that is executed before any test is run in this class. If multiple tests
            // are executed then it will still only be called once.
            TestUtilsDomain.SetupFixture();
        }

        [TearDown]
        public void TearDownTest()
        {
            //runs every time any testmethod is complete
        }

        #region DMRelationshipOneDirectionMustBeAssociation

        [Test]
        public void Test_Construct_ShouldSetupRuleWithRelationship()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedDefaultDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DMRelationshipManyToOneMustBeAssociation rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Test Result -----------------------
            Assert.IsNotNull(rule);
        }

        [Test]
        public void Test_Construct_ShouldSupport_IBusinessObjectRule()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedDefaultDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DMRelationshipManyToOneMustBeAssociation rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Test Result -----------------------
            TestUtilsShared.AssertIsInstanceOf<IBusinessObjectRule>(rule);
        }

//        [Test]
//        public void Test_Construct_WhenRelationshipIsNull_ShouldRaiseError()
//        {
//            //---------------Set up test pack-------------------
//            //---------------Assert Precondition----------------
//            //---------------Execute Test ----------------------
//            try
//            {
//                new DMRelationshipManyToOneMustBeAssociation(null);
//                Assert.Fail("expected ArgumentNullException");
//            }
//                //---------------Test Result -----------------------
//            catch (ArgumentNullException ex)
//            {
//                StringAssert.Contains("Value cannot be null", ex.Message);
//                StringAssert.Contains("dmRelationship", ex.ParamName);
//            }
//        }

        [Test]
        public void Test_Name_ShouldBeCorrect()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedDefaultDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string name = rule.Name;
            //---------------Test Result -----------------------
            Assert.AreEqual("DMRelationshipManyToOneMustBeAssociation", name);
        }

        [Test]
        public void Test_ErrorLevel_ShouldBeError()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedDefaultDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            ErrorLevel errorLevel = rule.ErrorLevel;
            //---------------Test Result -----------------------
            Assert.AreEqual(ErrorLevel.Error, errorLevel);
        }

        [Test]
        public void Test_Message_ShouldSetupRuleWithRelationship()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedValidDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            DMRelationshipManyToOneMustBeAssociation rule = new DMRelationshipManyToOneMustBeAssociation();
            relationship.RelationshipName = TestUtilsShared.GetRandomString();
            relationship.RelationshipType = TestUtilsShared.GetRandomEnum<RelationshipType>(RelationshipType.Association);
            //---------------Assert Precondition----------------
            Assert.IsNotNull(relationship.RelationshipType);
            Assert.IsNotNull(relationship.RelationshipName);
            Assert.IsNotNull(relationship.OwnerClassName);
            //---------------Execute Test ----------------------
            rule.IsValid(relationship);
            string message = rule.Message;
            //---------------Test Result -----------------------
            StringAssert.Contains("The Many to One " + relationship.RelationshipType + " Relationship '" + relationship.RelationshipName +
                                  "' on class '" + relationship.OwnerClassName + "' must be Association", message);
        }

        [Test]
        public void Test_IsValid_WhenHaveManyToOneComposition_ShouldFail()
        {
            //---------------Set up test pack-------------------
            IDMRelationship dmRelationship = CreateRelationship();
            SetAsManyToOne(dmRelationship);
            SetAsComposition(dmRelationship);
            SetAsAssocation(dmRelationship.ReverseRelationship);

            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Assert Precondition----------------
            AssertIsCompostion(dmRelationship);
            Assert.IsTrue(dmRelationship.IsManyToOne);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsFalse(isValid);
            TestUtilsShared.AssertStringNotEmpty(rule.Message, "rule.Message");
        }
        
        [Test]
        public void Test_IsValid_WhenHaveManyToOneAggregation_ShouldFail()
        {
            //---------------Set up test pack-------------------
            IDMRelationship dmRelationship = CreateRelationship();
            SetAsManyToOne(dmRelationship);
            SetAsAggregation(dmRelationship);
            SetAsAssocation(dmRelationship.ReverseRelationship);
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();

            //---------------Assert Precondition----------------
            AssertIsAggregation(dmRelationship);
            Assert.IsTrue(dmRelationship.IsManyToOne);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsFalse(isValid);
            TestUtilsShared.AssertStringNotEmpty(rule.Message, "rule.Message");
        }
        
        [Test]
        public void Test_IsValid_WhenHaveManyToOneAssociation_ShouldPass()
        {
            //---------------Set up test pack-------------------
            IDMRelationship dmRelationship = CreateRelationship();
            SetAsManyToOne(dmRelationship);
            SetAsAssocation(dmRelationship);
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();

            //---------------Assert Precondition----------------
            AssertIsAssociation(dmRelationship);
            Assert.IsTrue(dmRelationship.IsManyToOne);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid);
        }

        /// <summary>
        ///Even though this is an invalid relationship the
        /// rule will pass since the validation on the reverse relationship 
        /// will pick up the validation error.<br/>
        ///If we validated on this relationship it would result in two issues being 
        ///  raised for the same modelling error.
        /// </summary>
        [Test]
        public void Test_IsValid_WhenHaveOneToMany_Composition_ShouldPass()
        {

            //---------------Set up test pack-------------------
            IDMRelationship dmRelationship = CreateRelationship();
            SetAsOneToMany(dmRelationship);
            SetAsComposition(dmRelationship);
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Assert Precondition----------------
            Assert.IsTrue(dmRelationship.IsOneToMany, "Should be one to many");
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid);
        }
        /// <summary>
        /// Even though this is an invalid relationship the
        /// rule will pass since the validity of the OneToOne is
        /// checked independently of this rule <see cref="DMRelationshipManyToOneMustBeAssociation"/>
        /// </summary>
        [Test]
        public void Test_IsValid_WhenHaveOneToOne_Composition_ShouldPass()
        {
            //---------------Set up test pack-------------------
            IDMRelationship dmRelationship = CreateRelationship();
            SetAsOneToOne(dmRelationship);
            SetAsComposition(dmRelationship);
            IBusinessObjectRule rule = new DMRelationshipManyToOneMustBeAssociation();
            //---------------Assert Precondition----------------
            AssertIsCompostion(dmRelationship);
            Assert.IsTrue(dmRelationship.IsOneToOne);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid);
        }

        private static void AssertIsCompostion(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(dmRelationship.IsCompositionOrAggregation, "Relationship should be composition or aggregation");
        }

        private static void AssertIsAggregation(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(dmRelationship.IsCompositionOrAggregation, "Relationship should be composition or aggregation");
        }
        private static void AssertIsAssociation(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(!dmRelationship.IsCompositionOrAggregation, "Relationship should be association");
        }
        private static IDMRelationship CreateRelationship()
        {
            return MockRepository.GenerateMock<IDMRelationship>();
        }

        private static void SetAsAssocation(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsAssociation).Return(true);
        }

        private static void SetAsComposition(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsCompositionOrAggregation).Return(true);
        }

        private static void SetAsAggregation(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsCompositionOrAggregation).Return(true);
        }

        private static void SetAsManyToOne(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(true);
        }

        private static void SetAsOneToMany(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToMany).Return(true);
        }

        private static void SetAsOneToOne(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToOne).Return(true);
        }

        #endregion
    }
}
